﻿using FastData.Caching;
using System.Data.Linq.Mapping;
using System.Reflection;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using System;
using System.Data.Common;
using System.Linq.Expressions;

namespace FastData
{
    public abstract class QueryBase<T> : QueryBase where T : new()
    {
        internal QueryBase(DataAccess data)
            : base(data)
        {
            TableName = typeof(T).GetTableName();
        }

        internal void SetOrderBy<TKey>(Expression<Func<T, TKey>> expression, SortDirection sortDirection = SortDirection.NotSet)
        {
            SetOrderBy(expression.Body.GetMemberName(), sortDirection);
        }

        internal void SetWhere(Expression<Func<T, bool>> expression)
        {
            SetWhere(ParseExpression(expression.Body));
        }

        internal void SetField<TKey>(Expression<Func<T, TKey>> expression, object value)
        {
            AddField(expression.Body.GetMemberName(), value, true);
        }

        private string ParseExpression(Expression exp)
        {
            string expString = "?";
            if (exp is MemberExpression)
            {
                var memberExpression = exp as MemberExpression;
                if (memberExpression.Expression is ConstantExpression)
                {
                    var constantExpression = memberExpression.Expression as ConstantExpression;
                    var obj = constantExpression.Value;
                    object value;
                    if (memberExpression.Member.MemberType == MemberTypes.Field)
                    {
                        value = obj.GetType().GetField(memberExpression.Member.Name).GetValue(obj);
                    }
                    else if (memberExpression.Member.MemberType == MemberTypes.Property)
                    {
                        value = obj.GetType().GetProperty(memberExpression.Member.Name).GetValue(obj);
                    }
                    else
                    {
                        throw new Exception(memberExpression.Member.Name + " is " + memberExpression.Member.MemberType);
                    }
                    expString = Param(value);
                }
                else
                {
                    expString = Data.Provider.GetDbObjectName(memberExpression.Member.Name);
                }
            }
            else if (exp is UnaryExpression)
            {
                var unaryExpression = exp as UnaryExpression;
                expString = ParseExpression(unaryExpression.Operand);
            }
            else if (exp is ConstantExpression)
            {
                var constantExpression = exp as ConstantExpression;
                expString = Param(constantExpression.Value);
            }
            else if (exp is MethodCallExpression)
            {
                var methodCallExpression = exp as MethodCallExpression;
                if (methodCallExpression.Method.Name == "Contains")
                {
                    expString = ParseExpression(methodCallExpression.Object) + " like'%'+" + ParseExpression(methodCallExpression.Arguments[0]) + "+'%'";
                }
                if (methodCallExpression.Method.Name == "IsNullOrEmpty")
                {
                    expString = "(" + ParseExpression(methodCallExpression.Arguments[0]) + " IS NULL) OR (" + ParseExpression(methodCallExpression.Arguments[0]) + "='')";
                }
                expString = "(" + expString + ")";
            }
            else if (exp is BinaryExpression)
            {
                var binaryExpression = exp as BinaryExpression;
                if (binaryExpression.Right is ConstantExpression && (binaryExpression.Right as ConstantExpression).Value == null)
                {
                    if (binaryExpression.NodeType == ExpressionType.Equal)
                    {
                        expString = ParseExpression(binaryExpression.Left) + " IS NULL";
                    }
                    else if (binaryExpression.NodeType == ExpressionType.NotEqual)
                    {
                        expString = "NOT " + ParseExpression(binaryExpression.Left) + " IS NULL";
                    }
                }
                else
                {
                    expString = ParseExpression(binaryExpression.Left) + binaryExpression.NodeType.GetString() + ParseExpression(binaryExpression.Right);
                }
                expString = "(" + expString + ")";
            }
            else if (exp is InvocationExpression)
            {
                var invocationExpression = exp as InvocationExpression;
            }
            return expString;
        }

        private string Param(object value)
        {
            string name = "FastDataParameter" + Parameters.Count;
            AddParameter(name, value);
            return Data.Provider.GetDbParameterName(name);
        }
    }
}
